require("BnbConstants")
module("BnbLevel", package.seeall)

--[[
Contains the information about the current bnb level.
--]]
level = {}

local function newLevel ()
  level = {
    -- Map information (-1 goes for "uninitialized")
    MapInfo = {
      width = -1,
      height = -1,
      tileWidth = -1,
      tileHeight = -1
    },
    -- Properties
    Properties = {},
    -- Map data
    Data = {},
    -- Map data for the collision engine (collision map)
    CollisionData = {}
  }
end

local function loadHeader(map)
  -- Check consistency (magic number and a few significant fields).
  local mapError = ((map == nil) or (map.properties == nil) or (map.properties.magic == nil) or
  (map.properties.magic ~= BnbConstants.Level.BnbMagic) or (map.tilesets == nil) or (map.layers ==
    nil))

  if (mapError) then
    BnbConstants.bnbError = BnbConstants.Messages.Errors.Level.CONSISTENCY
    return false
  else
    print "Map consistency OK."
  end

  -- Load information
  level.MapInfo.version = map.version
  level.MapInfo.width = map.width
  level.MapInfo.height = map.height
  level.MapInfo.tileWidth = map.tilewidth
  level.MapInfo.tileHeight = map.tileheight
  level.Properties = map.properties

  return true
end

--[[
Given the tileset, build a map {Id->collision tags}.
--]]
local function buildTileColMap(tileset)

  if (tileset.name ~= BnbConstants.Level.TileSetName) then
    BnbConstants.bnbError = BnbConstants.Messages.Errors.Level.WRONG_TILESET
    BnbConstants.bnbErrorDetails = { tileset.name }
    return nil
  end

  tileMap = {}
  local k = 1
  local tiles = tileset.tiles

  while (k <= #tiles)
  do
    local tile = tiles[k]
    local id = tile.id
    local tags = tile.properties.collisionTags
    
    if (tags == nil) then
      BnbConstants.bnbError = BnbConstants.Messages.Errors.Level.COLTAGS_MISSING
      BnbConstants.bnbErrorDetails = { id }
      return nil
    end
    
    tileMap[id] = tags
    k = k + 1
  end

  return tileMap
end

local function buildCollisionMap(tileColMap, data)
  local colData = {}

  for i,v in pairs(data) do
    if (v == 0) then
      colData[i] = 0
    else
      colData[i] = tileColMap[v]
      if (colData == nil) then
        BnbConstants.bnbError = BnbConstants.Messages.Errors.Level.INVALID_TILE_ID
        BnbConstants.bnbErrorDetails = { v }
        return nil
      end
    end
  end

  return colData
end

local function loadData (map)
  if (#map.layers ~= BnbConstants.Level.LayersNumber)
  then
    BnbConstants.bnbError = BnbConstants.Messages.Errors.Level.NUMBER_OF_LAYERS
    BnbConstants.bnbErrorDetails = { #map.layers }
    return false
  end

  -- Build a table for associating a tile id with the collision flags,
  -- in order to build the collision map more easily.
  -- N.B. : take the first and only tileset which must be the BnB tileset.
  local tileColMap = buildTileColMap(map.tilesets[1])
  if (tileColMap == nil) then
    return false
  end

  for lay,contents in pairs(map.layers) do
    -- New layer indexed with its name.
    local name = contents.name
    -- Name must be one of the predefined names.
    local ok = false
    for i,expectName in pairs(BnbConstants.Level.LayersNames) do
      if (name == expectName) then
        ok = true
        break
      end
    end

    if (not ok) then
      BnbConstants.bnbError = BnbConstants.Messages.Errors.Level.LAYER_NAME
      return false
    end

    -- Copy the data as is.
    level.Data[name] = contents.data
    
    -- Make a transformation of the data to get the collision map.
    level.CollisionData[name] = buildCollisionMap(tileColMap, contents.data)
  end

  return true
end


--[[
Public function for loading a level.
--]]
function loadLevel (filename)
  dofile(filename)
  if map == nil then
    BnbConstants.bnbError = Messages.Errors.Level.MAP_NIL
    BnbConstants.bnbErrorDetails = { filename }
    return false
  end

  newLevel()
  
  return loadHeader(map) and loadData(map)
end

